from constants import *

class TilePart:
    '''
    inputs:
        val: tile type for this tile part
    defs:
        None
    '''
    def __init__(self, val):
        # constructor
        # returns: None
        self.val = val
        self.x = 0
        self.y = 0
    
class Tile:
    '''
    inputs:
        land: 3x3 array of tile types [row][column] 
        val: integer tile id
    defs:
        getTileMap
    '''
    def __init__(self, land, val):
        # constructor
        # returns: None
        self.land = land 
        self.val = val
        self.x = 0
        self.y = 0
        self.rotation = N
        
    def getTileMap(self):
        # creates a tile map to include all the tile types of this tiles
        # returns: created tile map
        tileMap = []
        for i in range(0, len(self.land)):
            for j in range(0, len(self.land[i])):
                part = TilePart(self.land[i][j])
                part.x = self.x + (i * len(self.land))
                part.y = self.y + (j * len(self.land))
                tileMap.append(part)
        return tileMap
        
    def rotate(self):
        # rotates this tile's land and increments orientation
        # returns: None
        newLand = []
        for i in range(0, len(self.land)):
            for j in range(0, len(self.land[i])):
                newLand[len(self.land) - j][i] = self.land[i][j]
        if self.orientation == N:
            self.orientation = W
        elif self.orientation == W:
            self.orientation = S
        elif self.orientation == S:
            self.orientation = E
        elif self.orientation == E:
            self.orientation = N
        else:
            #TODO: raise an error here
            self.orientation = N
        self.land = newLand
                      
class Feature:
    '''
    inputs:
        val: tile type for this feature
    defs:
        getRawTileMap
    '''
    def __init__(self, val):
        # constructor
        # returns: None
        self.val = val
        self.tiles = []
        self.tileMap = []
        self.occupants = {}
        self.closedNeighborCities = []
        self.status = OPEN
    
    def getRawTileMap(self):
        # creates a tile map to include all the tile types of this features tiles
        # returns: created tile map
        rawTileMap = []
        for t in self.tiles:
            for i in range(0, len(t.land)):
                for j in range(0, len(t.land[i])):
                    part = TilePart(t.land[i][j])
                    part.x = t.x + (i * len(t.land))
                    part.y = t.y + (j * len(t.land))
                    rawTileMap.append(part)
        return rawTileMap
        
class Follower:
    '''
    inputs:
        val: tile type this follower occupies
    defs:
        None
    '''
    def __init__(self, val):
        # constructor
        # returns: None
        self.val = val
        self.x = 0
        self.y = 0
    
class Board:
    '''
    inputs: 
        startingTile: Tile object that starts the board
        tileTypes: list of tile types in play
    defs:
        placeTile
        placeFollower
        validTilePlace
        validFollowerPlace
        updateFeatures
        closeFeatures
        checkFarms
        checkCityStatus
        checkRoadStatus
    '''
    def __init__(self, startingTile, tileTypes):
        # constructor
        # returns: None
        self.tiles = [startingTile]
        self.tileMap = []
        self.tileTypes = tileTypes
        self.followers = []
        self.features = []
        self.closedCities = []
        self.farms = []
        self.gameOver = False
    
    def placeTile(self, tile):
        # places tile on board if the move is valid and updates board features
        # returns: True if valid move
        valid = self.validTilePlace(tile)
        if valid:        
            self.tiles.append(tile)
            self.updateFeatures()
        return valid
    
    def placeFollower(self,follower):
        # places follower on board if the move is valid and checks if any features closed
        # returns: True if valid move
        valid = self.validFollowerPlace(follower)
        if valid: 
            self.followers.append(follower)
            self.closeFeatures()
        return valid
        
    def validTilePlace(self, tile):
        # places checks if tile is placeable
        # returns: True if placeable
        valid = True
        for t in self.tiles:
            if t.x == tile.x and t.y == tile.y:
                valid = False
            elif t.x == tile.x - 1 and t.y == tile.y: 
                valid = valid and t.land[1][2] == tile.land[1][0]
            elif t.x == tile.x + 1 and t.y == tile.y: 
                valid = valid and t.land[1][0] == tile.land[1][2]
            elif t.x == tile.x and t.y == tile.y + 1: 
                valid = valid and t.land[2][1] == tile.land[0][1]
            elif t.x == tile.x and t.y == tile.y - 1: 
                valid = valid and t.land[0][1] == tile.land[2][1]
            else:
                pass
        return valid
    
    def validFollowerPlace(self, follower):
        # places checks if follower is placeable
        # returns: True if placeable
        valid = True
        # TODO: add this logic
        return valid
        
    def updateFeatures(self):
        # updates feature tileMap in the features list for board, checks for duplicates
        # returns: None
        # TODO: add this logic
        pass 
        
    def closeFeatures(self):
        # closes features if they are finished
        # returns: list of closed feature tuples (Feature object, points awarded, [players who scored], {players : number of followers returned})
        closedFeatures = []
        for f in self.features:
            if f.status != CLOSED:
                score = 0
                maxOccupants = 0
                scoringPlayers = []
                occupyingPlayers = {}
                if f.val == M and M in self.tileTypes:
                    if len(f.tiles) == 9 or self.gameOver:
                        f.status = CLOSED
                    score = len(f.tiles)
                elif f.val == R and R in self.tileTypes:
                    self.checkRoadStatus(f)
                    score = len(f.tiles)
                elif f.val == C and C in self.tileTypes:
                    numPendants = 0
                    self.checkCityStatus(f)
                    if f.status == CLOSED:
                        scoreInc = 2
                        self.closedCities.append(f)
                    else:
                        scoreInc = 1
                    for t in f.tiles:
                        score = score + scoreInc
                        for row in t.land:
                            if P in row:
                                score = score + scoreInc
                elif f.val == F and F in self.tileTypes:
                    self.farms.append(f)
                else:
                    print 'Feature Type: %s not supported or not in allowable tiles, need to add' % (f.val)
                for k in f.occupants.keys():
                    occupyingPlayers[k] = f.occupants[k]
                    maxOccupants = max(maxOccupants, f.occupants[k])
                for k in f.occupants.keys():
                    if f.occupants[k] == maxOccupants:
                        scoringPlayers.append(k)
                if f.status == CLOSED:
                    closedFeatures.append((f, score, scoringPlayers, occupyingPlayers))
        self.checkFarms()
        if self.gameOver:
            for f in self.farms:
                f.status = CLOSED
                score = len(f.closedNeighborCities) * 3
                maxOccupants = 0
                scoringPlayers = []
                occupyingPlayers = {}
                for k in f.occupants.keys():
                    occupyingPlayers[k] = f.occupants[k]
                    maxOccupants = max(maxOccupants, f.occupants[k])
                for k in f.occupants.keys():
                    if f.occupants[k] == maxOccupants:
                        scoringPlayers.append(k)
                closedFeatures.append((f, score, scoringPlayers, occupyingPlayers))        
        return closedFeatures
    
    def checkFarms(self):
        # updates farm features neighboring closed cities
        # returns: None
        # TODO: add this logic
        pass 
    
    def checkCityStatus(self, feature):
        # updates city feature status 
        # returns: None
        # TODO: add this logic
        pass 
    
    def checkRoadStatus(self, feature):
        # updates road feature status 
        # returns: None
        # TODO: add this logic
        pass 
        